package com.veinhorn.scrollgalleryview;

import com.veinhorn.scrollgalleryview.builder.GalleryBuilder;
import com.veinhorn.scrollgalleryview.builder.GalleryBuilderImpl;
import com.veinhorn.scrollgalleryview.loader.MediaLoader;
import com.veinhorn.scrollgalleryview.util.LogUtil;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Image;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.PageSlider;
import ohos.agp.components.ScrollView;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.transition.Transition;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.NotExistException;
import ohos.global.resource.Resource;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * ScrollGalleryView
 *
 * @author veinhorn
 * @since 2021-03-24
 */
public class ScrollGalleryView extends DirectionalLayout {
    private static final int MARGIN = 10;
    private static final int NUM2 = 2;
    private Context mContext;
    private ScreenSlidePagerAdapter pagerAdapter;
    private List<MediaInfo> mListOfMedia;

    // Options width and height in pixels
    private int mThumbnailSize;
    private boolean mIsZoomEnabled;
    private boolean mIsThumbnailsHidden;
    private boolean mIsHideThumbnailsOnClick;
    private int mHideThumbnailsAfterDelay = 0;

    // Views
    private MyDirectionalLayout thumbnailsContainer;
    private ScrollView horizontalScrollView;
    private HackyViewPager viewPager;

    // Transitions
//    private Transition mThumbnailsTransition;
//    private boolean isUseDefaultThumbnailsTransition;

    // Listeners
    private final PageSlider.PageChangedListener viewPagerChangeListener = new PageSlider.PageChangedListener() {
        @Override
        public void onPageSliding(int i, float v, int i1) {
        }

        @Override
        public void onPageSlideStateChanged(int i) {
        }

        @Override
        public void onPageChosen(int position) {
            scroll(thumbnailsContainer.getComponentAt(position));
        }
    };

    private final Component.ClickedListener thumbnailOnClickListener = new ClickedListener() {
        @Override
        public void onClick(Component component) {
            scroll(component);
            viewPager.setCurrentPage(component.getId(), true);
        }
    };

    private OnImageClickListener mOnImageClickListener;
    private OnImageLongClickListener mOnImageLongClickListener;

    /**
     * We should create OnImageClickListener to wrap our provided OnImageClickListener,
     * otherwise it will be null when we pass it to PagerAdapter. Also it used to wrap
     * code which is responsible for showing/hiding thumbnails when user click on image
     */
    private OnImageClickListener innerOnImageClickListener = new OnImageClickListener() {
        @Override
        public void onClick(int position) {
            if (mIsHideThumbnailsOnClick) {
                if (mIsThumbnailsHidden) {
                    showThumbnails();
                    mIsThumbnailsHidden = false;
                } else {
                    hideThumbnails();
                    mIsThumbnailsHidden = true;
                }
            }
            if (mOnImageClickListener != null) {
                mOnImageClickListener.onClick(position);
            }
        }
    };

    private OnImageLongClickListener innerOnImageLongClickListener = new OnImageLongClickListener() {
        @Override
        public void onClick(int position) {
            if (mOnImageLongClickListener != null) {
                mOnImageLongClickListener.onClick(position);
            }
        }
    };

    /**
     * ScrollGalleryView
     *
     * @param context
     * @param attrs
     */
    public ScrollGalleryView(Context context, AttrSet attrs) {
        super(context, attrs);
        this.mContext = context;
        mListOfMedia = new ArrayList<>();

        setOrientation(VERTICAL);

        Component cpt = LayoutScatter.getInstance(getContext())
                .parse(ResourceTable.Layout_scroll_gallery_view, null, true);
        viewPager = (HackyViewPager) cpt.findComponentById(ResourceTable.Id_viewPager);
        horizontalScrollView = (ScrollView) cpt.findComponentById(ResourceTable.Id_thumbnails_scroll_view);

        thumbnailsContainer = (MyDirectionalLayout) cpt.findComponentById(ResourceTable.Id_thumbnails_container);
        thumbnailsContainer.setPadding(getDisplaySize().width / NUM2, 0, getDisplaySize().width / NUM2, 0);
        addComponent(cpt);
    }

    /**
     * setFragmentManager
     *
     * @param medias
     * @return ScrollGalleryView
     */
    public ScrollGalleryView setFragmentManager(List<MediaInfo> medias) {
        initializeViewPager(medias);
        hideThumbnailsAfterDelay(mHideThumbnailsAfterDelay);
        return this;
    }

    /**
     * getViewPager
     *
     * @return inner ViewPager
     */
    public PageSlider getViewPager() {
        return viewPager;
    }

    /**
     * Set up OnImageClickListener for your gallery images
     * You should set OnImageClickListener only before setFragmentManager call!
     *
     * @param onImageClickListener which is called when you click on image
     * @return ScrollGalleryView
     */
    public ScrollGalleryView addOnImageClickListener(OnImageClickListener onImageClickListener) {
        this.mOnImageClickListener = onImageClickListener;
        return this;
    }

    /**
     * addOnImageLongClickListener
     *
     * @param onImageLongClickListener
     * @return ScrollGalleryView
     */
    public ScrollGalleryView addOnImageLongClickListener(OnImageLongClickListener onImageLongClickListener) {
        this.mOnImageLongClickListener = onImageLongClickListener;
        return this;
    }

    /**
     * Set up OnPageChangeListener for internal ViewPager
     *
     * @param listener which is used by internal ViewPager
     */
    public void addOnPageChangeListener(final PageSlider.PageChangedListener listener) {
        viewPager.removePageChangedListener(listener);
        viewPager.addPageChangedListener(new PageSlider.PageChangedListener() {
            @Override
            public void onPageSliding(int position, float positionOffset, int positionOffsetPixels) {
                listener.onPageSliding(position, positionOffset, positionOffsetPixels);
            }

            @Override
            public void onPageSlideStateChanged(int state) {
                listener.onPageSlideStateChanged(state);
            }

            @Override
            public void onPageChosen(int position) {
                scroll(thumbnailsContainer.getComponentAt(position));
                listener.onPageChosen(position);
            }
        });
    }

    /**
     * addMedia
     *
     * @param mediaInfo
     * @return ScrollGalleryView
     * @throws NullPointerException
     */
    public ScrollGalleryView addMedia(MediaInfo mediaInfo) {
        if (mediaInfo == null) {
            throw new NullPointerException("Infos may not be null!");
        }

        return addMedia(Collections.singletonList(mediaInfo));
    }

    /**
     * addMedia
     *
     * @param infos
     * @return ScrollGalleryView
     * @throws NullPointerException
     */
    public ScrollGalleryView addMedia(List<MediaInfo> infos) {
        if (infos == null) {
            throw new NullPointerException("Infos may not be null!");
        }

        for (MediaInfo info : infos) {
            mListOfMedia.add(info);

            final Image thumbnail = addThumbnail(getDefaultThumbnail());
            info.getLoader().loadThumbnail(getContext(), thumbnail, new MediaLoader.SuccessCallback() {
                @Override
                public void onSuccess() {
                    thumbnail.setScaleMode(Image.ScaleMode.CENTER);
                }
            });

            pagerAdapter.notifyDataChanged();
        }
        return this;
    }

    /**
     * Set the current item displayed in the view pager.
     *
     * @param index an zero-based index
     * @return ScrollGalleryView
     */
    public ScrollGalleryView setCurrentItem(int index) {
        viewPager.setCurrentPage(index, false);
        return this;
    }

    /**
     * getCurrentItem
     *
     * @return int
     */
    public int getCurrentItem() {
        return viewPager.getCurrentPage();
    }

    /**
     * setThumbnailSize
     *
     * @param thumbnailSize
     * @return ScrollGalleryView
     */
    public ScrollGalleryView setThumbnailSize(int thumbnailSize) {
        this.mThumbnailSize = thumbnailSize;
        return this;
    }

    /**
     * setZoom
     *
     * @param isZoomEnabled
     * @return ScrollGalleryView
     */
    public ScrollGalleryView setZoom(boolean isZoomEnabled) {
        this.mIsZoomEnabled = isZoomEnabled;
        return this;
    }

    /**
     * If you enabled this option, hideThumbnailsOnClick() method will not work
     *
     * @param isThumbnailsHidden hides thumbnails container
     * @return ScrollGalleryView
     */
    public ScrollGalleryView withHiddenThumbnails(boolean isThumbnailsHidden) {
        if (this.mIsThumbnailsHidden && !isThumbnailsHidden) {
            showThumbnails();
        } else if (!this.mIsThumbnailsHidden && isThumbnailsHidden) {
            hideThumbnails();
        }
        this.mIsThumbnailsHidden = isThumbnailsHidden;

        return this;
    }

    /**
     * Keep in mind that this method do not work with enabled isThumbnailsHidden option
     *
     * @param isHideThumbnailsOnClick hides thumbnails container on image click
     * @return ScrollGalleryView
     */
    public ScrollGalleryView hideThumbnailsOnClick(boolean isHideThumbnailsOnClick) {
        if (!mIsThumbnailsHidden) {
            this.mIsHideThumbnailsOnClick = isHideThumbnailsOnClick;
            if (isHideThumbnailsOnClick) {
//                this.isUseDefaultThumbnailsTransition = true;
            }
        }
        return this;
    }

    /**
     * Keep in mind that this method do not work with enabled isThumbnailsHidden option
     *
     * @param isHideThumbnailsOnClick hides thumbnails container on image click
     * @param thumbnailsTransition null is used to disable transation
     * @return ScrollGalleryView
     */
    public ScrollGalleryView hideThumbnailsOnClick(boolean isHideThumbnailsOnClick, Transition thumbnailsTransition) {
        if (!mIsThumbnailsHidden) {
            this.mIsHideThumbnailsOnClick = isHideThumbnailsOnClick;
//            this.mThumbnailsTransition = thumbnailsTransition;
        }
        return this;
    }

    /**
     * Automatically hide thumbnails container after specified delay
     *
     * @param hideThumbnailsAfterDelay delay in ms
     * @return ScrollGalleryView
     */
    public ScrollGalleryView hideThumbnailsAfter(int hideThumbnailsAfterDelay) {
        if (!mIsThumbnailsHidden) {
            this.mHideThumbnailsAfterDelay = hideThumbnailsAfterDelay;
        }
        return this;
    }

    /**
     * showThumbnails
     */
    public void showThumbnails() {
        horizontalScrollView.setVisibility(VISIBLE);
        hideThumbnailsAfterDelay(mHideThumbnailsAfterDelay);
    }

    /**
     * hideThumbnails
     */
    public void hideThumbnails() {
        horizontalScrollView.setVisibility(HIDE);
    }

    /**
     * Remove an media from the gallery
     *
     * @param position media's position to remove
     */
    public void removeMedia(int position) {
        if (position >= mListOfMedia.size() || position < 0) {
            return;
        }
        removeThumbnail(position);
    }

    /**
     * from
     *
     * @param galleryView
     * @return GalleryBuilder
     */
    public static GalleryBuilder from(ScrollGalleryView galleryView) {
        return new GalleryBuilderImpl(galleryView);
    }

    private void hideThumbnailsAfterDelay(int delay) {
        SetHorizontalScrollViewTask runAble = new SetHorizontalScrollViewTask();
        EventRunner runner = EventRunner.create(false);
        EventHandler myHandler = new EventHandler(runner);
        myHandler.postTask(runAble, delay);
    }

    private PixelMapElement getDefaultThumbnail() {
        PixelMapElement pixBg = null;
        try {
            Resource bgResource = mContext.getResourceManager().getResource(ResourceTable.Media_placeholder_image);
            pixBg = new PixelMapElement(bgResource);
        } catch (IOException e) {
            LogUtil.error("PixelMapElement-IOException", e.getMessage());
        } catch (NotExistException e) {
            LogUtil.error("PixelMapElement-NotExistException", e.getMessage());
        }
        return pixBg;
    }

    private DisplayAttributes getDisplaySize() {
        DisplayAttributes displayAttributes = null;
        Optional<Display> display = DisplayManager.getInstance().getDefaultDisplay(this.getContext());
        if (display.isPresent()) {
            displayAttributes = display.get().getAttributes();
        }
        return displayAttributes;
    }

    private void removeThumbnail(int position) {
        Component thumbnail = thumbnailsContainer.getComponentAt(position);
        if (thumbnail == null) {
            return;
        }
        thumbnailsContainer.removeComponent(thumbnail);
    }

    private Image addThumbnail(PixelMapElement image) {
        DirectionalLayout.LayoutConfig lp = new DirectionalLayout.LayoutConfig(mThumbnailSize, mThumbnailSize);
        lp.setMargins(MARGIN, MARGIN, MARGIN, MARGIN);
        Image thumbnailView = createThumbnailView(lp, image);
        thumbnailsContainer.addComponent(thumbnailView);
        return thumbnailView;
    }

    private Image createThumbnailView(DirectionalLayout.LayoutConfig lp, PixelMapElement thumbnail) {
        Image thumbnailView = new Image(mContext);
        thumbnailView.setLayoutConfig(lp);
        thumbnailView.setImageElement(thumbnail);
        thumbnailView.setId(mListOfMedia.size() - 1);
        thumbnailView.setClickedListener(thumbnailOnClickListener);
        thumbnailView.setScaleMode(Image.ScaleMode.CENTER);
        return thumbnailView;
    }

    private void initializeViewPager(List<MediaInfo> medias) {
        int size = medias.size();
        LogUtil.error("initializeViewPager", "长度" + size);
        pagerAdapter = new ScreenSlidePagerAdapter(mContext,
                medias,
                mIsZoomEnabled,
                innerOnImageClickListener,
                innerOnImageLongClickListener);

        viewPager.setPageCacheSize(medias.size());
        viewPager.setProvider(pagerAdapter);
        viewPager.addPageChangedListener(viewPagerChangeListener);
    }

    private void scroll(Component thumbnail) {
        // 计算当前点击的控件距离左边的位置，从而滑动ScrollView
        int left = thumbnail.getLeft() + thumbnail.getWidth() / NUM2;
        horizontalScrollView.scrollTo(left, 0);
    }

    private int calculateInSampleSize(int imgWidth, int imgHeight, int maxWidth, int maxHeight) {
        int inSampleSize = 1;
        while (imgWidth / inSampleSize > maxWidth || imgHeight / inSampleSize > maxHeight) {
            inSampleSize *= NUM2;
        }
        return inSampleSize;
    }

    /**
     * Runnable
     *
     * @since 2021-05-13
     */
    private class SetHorizontalScrollViewTask implements Runnable {
        @Override
        public void run() {
            hideThumbnails();
            mIsThumbnailsHidden = !mIsThumbnailsHidden;
        }
    }

    /**
     * OnImageClickListener
     *
     * @since 2021-03-24
     */
    public interface OnImageClickListener {
        /**
         * onClick
         *
         * @param position
         */
        void onClick(int position);
    }

    /**
     * OnImageLongClickListener
     *
     * @since 2021-03-24
     */
    public interface OnImageLongClickListener {
        /**
         * onClick
         *
         * @param position
         */
        void onClick(int position);
    }
}
